using System.Collections.Generic;
using System.Linq;
using System.Text;
using NFreeMarker.Template;

namespace NFreeMarker.Core
{
    public sealed class HashLiteral : Expression // NOTE: Was internal
    {
        private readonly List<Expression> _keys, _values;
        private readonly int _size;

        internal HashLiteral(List<Expression> keys, List<Expression> values)
        {
            _keys = keys;
            _values = values;
            _size = keys.Count;
            keys.TrimExcess();
            values.TrimExcess();
        }

        protected override ITemplateModel _getAsTemplateModel(Environment env)
        {
            return new SequenceHash(this, env);
        }

        public override string GetCanonicalForm()
        {
            var buf = new StringBuilder("{");
            for (int i = 0; i < _size; i++)
            {
                Expression key = _keys[i];
                Expression value = _values[i];
                buf.Append(key.GetCanonicalForm());
                buf.Append(" : ");
                buf.Append(value.GetCanonicalForm());
                if (i != _size - 1)
                {
                    buf.Append(",");
                }
            }
            buf.Append("}");
            return buf.ToString();
        }

        internal override bool IsLiteral()
        {
            if (ConstantValue != null)
            {
                return true;
            }
            for (int i = 0; i < _size; i++)
            {
                Expression key = _keys[i];
                Expression value = _values[i];
                if (!key.IsLiteral() || !value.IsLiteral())
                {
                    return false;
                }
            }
            return true;
        }


        protected override Expression DeepCloneImpl(string name, Expression subst)
        {
            List<Expression> clonedKeys = _keys.ToList();
            for (int i = 0; i < clonedKeys.Count; i++)
            {
                clonedKeys[i] = clonedKeys[i].DeepClone(name, subst);
            }

            List<Expression> clonedValues = _values.ToList();
            for (int i = 0; i < clonedValues.Count; i++)
            {
                clonedValues[i] = clonedValues[i].DeepClone(name, subst);
            }

            return new HashLiteral(clonedKeys, clonedValues);
        }

        private class SequenceHash : ITemplateHashModelEx
        {
            private readonly HashLiteral _hashLiteral;
            private readonly Dictionary<string, ITemplateModel> _keyMap; // maps keys to integer offset
            private readonly ITemplateCollectionModel _keyCollection; // ordered lists of keys
            private readonly ITemplateCollectionModel _valueCollection; // ordered lists of values

            internal SequenceHash(HashLiteral hashLiteral, Environment env)
            {
                _hashLiteral = hashLiteral;
                _keyMap = new Dictionary<string, ITemplateModel>();
                var keyList = new List<string>(_hashLiteral._size);
                var valueList = new List<ITemplateModel>(_hashLiteral._size);
                for (int i = 0; i < _hashLiteral._size; i++)
                {
                    Expression keyExp = _hashLiteral._keys[i];
                    Expression valExp = _hashLiteral._values[i];
                    string key = keyExp.GetStringValue(env);
                    ITemplateModel value = valExp.GetAsTemplateModel(env);
                    AssertNonNull(value, valExp, env);
                    _keyMap.Add(key, value);
                    keyList.Add(key);
                    valueList.Add(value);
                }
                _keyCollection = new CollectionAndSequence(new SimpleSequence(keyList));
                _valueCollection = new CollectionAndSequence(new SimpleSequence(valueList));
            }

            public int Size()
            {
                return _hashLiteral._size;
            }

            public ITemplateCollectionModel Keys()
            {
                return _keyCollection;
            }

            public ITemplateCollectionModel Values()
            {
                return _valueCollection;
            }

            public ITemplateModel Get(string key)
            {
                return _keyMap[key];
            }

            public bool IsEmpty()
            {
                return _hashLiteral._size == 0;
            }
        }
    }
}